package network;

import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import config.Configure;

import util.Pair;

public class OutputConnectionPool {
	private ConcurrentMap<Pair<InetAddress, Integer>, OutputTcpConnection> connections = new ConcurrentHashMap<Pair<InetAddress, Integer>, OutputTcpConnection>();
	private Timer timer = new Timer();

	public OutputConnectionPool() {
		timer.schedule(new ConnectionCleaner(),
				Configure.getRpcTimeout() * 10,
				Configure.getRpcTimeout() * 10);
	}

	public void putOutputConnection(Socket socket) {
		Pair<InetAddress, Integer> key;
		try {
			key = new Pair<InetAddress, Integer>(
					 InetAddress.getByAddress(socket.getInetAddress().getAddress()), socket.getPort());
			OutputTcpConnection output = new OutputTcpConnection(socket);
			if (connections.putIfAbsent(key, output) == null) {
				output.start();
			}
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
	}

	public OutputTcpConnection getOutputTcpConnection(InetAddress addr, int port) {
		Pair<InetAddress, Integer> key = new Pair<InetAddress, Integer>(addr,
				port);
		OutputTcpConnection ret = connections.get(key);
		if (ret == null) {
			OutputTcpConnection output = new OutputTcpConnection(addr, port);

			if (connections.putIfAbsent(key, output) != null) {
				output.write(OutputTcpConnection.CLOSE_MESSAGE);
			} else {
				if (output.connect())
					output.start();
				else
					return null;
			}
			ret = connections.get(key);
		}
		return ret;
	}

	private class ConnectionCleaner extends TimerTask {
		@Override
		public void run() {
			List<Pair<InetAddress, Integer>> delConnections = new LinkedList<Pair<InetAddress, Integer>>();
			for (Entry<Pair<InetAddress, Integer>, OutputTcpConnection> entry : connections
					.entrySet()) {
				if (entry.getValue().getIdleTimeInMillis() > Configure
						.getConnectionMaxIdleTime()) {
					delConnections.add(entry.getKey());
				}
			}

			for (Pair<InetAddress, Integer> addr : delConnections) {
				OutputTcpConnection conn = connections.get(addr);
				if (conn.getIdleTimeInMillis() > Configure
						.getConnectionMaxIdleTime()) {
					conn = connections.remove(addr);
					conn.write(OutputTcpConnection.CLOSE_MESSAGE);
				}
			}
		}
	}

}
